package org.nlp2rdf.navigator.benchmark;

import com.google.common.base.Joiner;
import com.google.common.collect.Sets;
import org.aksw.commons.util.experiments.ExperimentStatisticsCollector;
import org.aksw.commons.util.experiments.Static;
import org.aksw.commons.util.experiments.Table;
import org.dllearner.core.EvaluatedDescription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.DecimalFormat;
import java.util.Random;
import java.util.Set;

/**
 * Created by Claus Stadler
 * Date: Oct 15, 2010
 * Time: 4:56:52 PM
 */
public class SebsKFoldCrossValidationExperiment<E, D>
    extends KFoldCrossValidationExperiment<E, D>
{
    private String experimentName;
    private ExperimentStatisticsCollector collector;

    private static final Logger logger = LoggerFactory.getLogger(SebsKFoldCrossValidationExperiment.class);


    public Table getResult()
    {
        return collector.getTable();
    }

    public static <E, D> SebsKFoldCrossValidationExperiment<E, D> create(String experimentName, Sample<E> all, KFoldCollection<E> kFolds, int numPhases, Random random, IGenericLearner<E, D> learner, IGenericClassifier<E, D> classifier)
    {
        return new SebsKFoldCrossValidationExperiment<E, D>(experimentName, all, kFolds, numPhases, random, learner, classifier);
    }

    public SebsKFoldCrossValidationExperiment(String experimentName, Sample<E> all, KFoldCollection<E> kFolds, int numPhases, Random random, IGenericLearner<E, D> learner, IGenericClassifier<E, D> classifier)
    {
        super(all, kFolds, numPhases, random, learner, classifier);

        collector = new ExperimentStatisticsCollector(experimentName, numPhases);
        this.experimentName = experimentName;
    }


    public static double fMeasure(double precision, double recall)
    {
        return 2 * precision * recall / (double)(precision + recall);
    }

    private static DecimalFormat formatter = new DecimalFormat("0.######");

    public static String humanReadable(double value)
    {
        return formatter.format(value);
    }


    @Override
    public void collectStats()
    {
        EvaluatedDescription learnedDescription = (EvaluatedDescription)currentPhase.getLearnedDescription();

        logger.info("Stats after Fold/Phase: " + Joiner.on("/").join(currentFoldId, currentPhaseId));
        logger.info("-------------------------");
        logger.info("Learned: " + ((EvaluatedDescription)currentPhase.getLearnedDescription()).getDescription().toKBSyntaxString());

        logger.info("Positives: " + ClausBenchmark.toMyString(currentPhase.getCurrent().getPositives()));
        logger.info("Negatives: " + ClausBenchmark.toMyString(currentPhase.getCurrent().getNegatives()));

        Set<E> examples = currentPhase.getExamples();
        Set<E> totalTruePositives = Sets.intersection(all.getPositives(), examples);
        Set<E> totalFalsePositives = Sets.difference(examples, all.getPositives());


        double totalPrecision = totalTruePositives.size() / (double)examples.size();
        double totalRecall =  totalTruePositives.size() / (double)all.getPositives().size();
        double totalFMeasure = fMeasure(totalPrecision, totalRecall);

        collector.getMonitor("totalPrecision", currentPhaseId, Static.Units.PERCENTAGE).add(totalPrecision);
        collector.getMonitor("totalRecall", currentPhaseId, Static.Units.PERCENTAGE).add(totalRecall);
        collector.getMonitor("totalFMeasure", currentPhaseId, Static.Units.PERCENTAGE).add(totalFMeasure);

        collector.getMonitor("accuracy", currentPhaseId, Static.Units.PERCENTAGE).add(learnedDescription.getAccuracy());

        

/*
        if(Sets.difference(examples, currentFold.getTrainSample().getPositives()).size() == 0) {
            System.out.println("WTF");
        }
*/

        //logger.info("test samples: " + currentFold.getTestSample().getPositives());

        Set<E> testTruePositives = Sets.intersection(currentFold.getTestSample().getPositives(), examples);
        //logger.info("test true postives:" + testTruePositives);

        logger.info("Missed positives: " +  ClausBenchmark.toMyString(Sets.difference(all.getPositives(), totalTruePositives)));
        logger.info("Missed test samples: " + ClausBenchmark.toMyString(Sets.difference(currentFold.getTestSample().getPositives(), testTruePositives)));


        // FIXME description.getAccuracy();

        double testPrecision = testTruePositives.size() / (double)Sets.difference(examples, currentFold.getTrainSample().getPositives()).size();
        double testRecall =  testTruePositives.size() / (double)currentFold.getTestSample().getPositives().size();
        double testFMeasure = fMeasure(testPrecision, testRecall);


        collector.getMonitor("testPrecision", currentPhaseId, Static.Units.PERCENTAGE).add(testPrecision);
        collector.getMonitor("testRecall", currentPhaseId, Static.Units.PERCENTAGE).add(testRecall);
        collector.getMonitor("testFMeasure", currentPhaseId, Static.Units.PERCENTAGE).add(testFMeasure);


        logger.info("Total precision/recall/fMeasure: " + Joiner.on("/").join(humanReadable(totalPrecision), humanReadable(totalRecall), humanReadable(totalFMeasure)));
        logger.info("Test precision/recall/fMeasure: " + Joiner.on("/").join(humanReadable(testPrecision), humanReadable(testRecall), humanReadable(testFMeasure)));


        //table.
        //TableRowColumn row = new TableRowColumn(experimentName, "precision");


        //org.aksw.commons.util.experiments.Table table = collector.getTable();
        //table.
        //table.
        //this.
    }
}
